<?php
declare (strict_types=1);

namespace app\controller;

use app\BaseController;
use think\facade\Cache;
use think\facade\Db;

class base extends BaseController
{
    static private $static_model;
    static private $static_id;
    protected $model = '';
    //主表paginate查询的字段
    protected $idName = 'id';
    protected $fieldRaw = '*';
    //验证字段
    protected $order = '';
    //验证规则
    protected $rules = [];
    //获取参数
    protected $message = [];
    //判断状态字段
    protected $data = [];
    //关联模型方法名 join必须与prefix一起用
    protected $status = [];
    protected $join = [];
    //模型名
    protected $with = [];
    protected $prefix = '';
    protected $account_info = '';
    protected $where = [];
    protected $return_res = '';
    protected $select = false;
    protected $redis = true;
    protected $redis_time = 30;
    protected $debug = false;
    protected $del = [];
    protected $id;
    protected $filter = [];
    protected $field_info = '*';

    static private function batch_func($info)
    {
        $pk2 = self::$static_model->saveAll($info);
        return array_column($pk2->toArray(), self::$static_id);

    }

    public function index()
    {
        if ($this->request->isPost()) {
            isset_json($this->data[config('base.fields')]);
            isset_json($this->data[config('base.fields_or')]);
            isset_json($this->data[config('base.custom')]);
            isset_json($this->data[config('base.search')]);
            isset_json($this->data[config('base.order')]);
            isset_set($this->data[config('base.page')], 1);
            isset_set($this->data[config('base.limit')], 10);
            if (!empty($this->data[config('base.custom')])) {
                foreach ($this->data[config('base.custom')] as $k => $v) {
                    if ($v[0] == 'rt') {
                        $this->data[config('base.custom')][$k][0] = '>=';
                    }
                    if ($v[0] == 'lt') {
                        $this->data[config('base.custom')][$k][0] = '<=';
                    }
                    if ($v[0] == 'r') {
                        $this->data[config('base.custom')][$k][0] = '>';
                    }
                    if ($v[0] == 'l') {
                        $this->data[config('base.custom')][$k][0] = '<';
                    }
                    if ($v[0] == 'no') {
                        $this->data[config('base.custom')][$k][0] = '<>';
                    }
                }
            }
            $query = $this->model;
            $query = $this->before_func($query);
            if (count($this->with) == 1) {
                $query = $query->hasWhere(array_keys($this->with)[0], function ($query) {
                    $this->res_func($query);
                }, $this->fieldRaw);
                $query = $query
                    ->with($this->with);
                if (count($this->status) > 0) {
                    foreach ($this->status as $key => $v) {
                        $query->where(all_ucwords($this->prefix) . '.' . $key, $v);
                    }
                }
            } else {
                if (is_array($this->fieldRaw)) {
                    $this->fieldRaw = implode(',', $this->fieldRaw);
                    $query = $query->field($this->fieldRaw, true);
                } else {
                    $query = $query->fieldRaw($this->fieldRaw);
                }
                $query = $this->fields_func($query);
                if (count($this->join) > 0) {
                    $query->withJoin($this->join, 'left');
                }
                $this->res_func($query);
            }
            try {
                if (judge($this->data['select']) && $this->select) {
                    $res = $query
                        ->selet()->toArray();
                    api_exit($res);
                }
                if ($this->redis) {
                    ksort($this->data);
                    $md5 = md5(json_encode($this->data));
                    if (Cache::store('redis')->get(get_called_class() . $md5)) {
                        api_exit(Cache::store('redis')->get(get_called_class() . $md5));
                    }
                }
                if ($this->debug) {
                    $res = $query->buildSql();
                    dump($res);
                    die;
                }
                $res = $query
                    ->paginate($this->data[config('base.limit')], false, ['page' => $this->data[config('base.page')]])->toArray();
                if ($this->redis) {
                    Cache::store('redis')->set(get_called_class() . $md5, $res, $this->redis_time);
                }
                if (judge($this->return_res)) {
                    if (empty($res['data'])) {
                        api_exit($res);
                    }
                    return $res;
                }
                api_exit($res);
            } catch (\Exception $exception) {
                uapi_exit($exception->getMessage());
            }

        }
    }

    public function before_func($query)
    {
        return $query;
    }

    public function res_func($query)
    {
        if (!empty($this->data[config('base.fields')])) {
            foreach ($this->data[config('base.fields')] as $k => $v) {
                if (pan_empty($v)) {
                    $query->where($k, $v);
                }
            }
        }
        $query = $this->fields_or_func($query);
        if (!empty($this->data[config('base.fields_or')])) {
            foreach ($this->data[config('base.fields_or')] as $k => $v) {
                if (pan_empty($v)) {
                    $query->where(function ($querySelect) use ($k, $v) {
                        $querySelect->whereOr($k, $v);
                    });
                }
            }
        }
        $query = $this->custom_func($query);
        if (!empty($this->data[config('base.custom')])) {
            foreach ($this->data[config('base.custom')] as $k => $v) {
                $query->where($k, $v[0], $v[1]);
            }
        }
        $query = $this->search_func($query);
        if (!empty($this->data[config('base.search')])) {
            $search = $this->data[config('base.search')];
            $query = $query->
            where(function ($querySelect) use ($search) {
                foreach ($search as $k => $v) {
                    if (pan_empty($v)) {
                        $querySelect->whereOr($k, 'like', '%' . $v . '%');
                    }
                }
            });
        }
        if (count($this->where) > 0) {
            foreach ($this->where as $k => $v) {
                if (is_array($v)) {
                    $query->where($k, $v[0], $v[1]);
                } else {
                    $query->where($k, $v);
                }
            }
        }
        $this->status_func($query);
        $query = $this->other_function($query);
        if (!empty($this->order)) {
            $query->order($this->order);
        } else {
            $query->order($this->data[config('base.order')]);
        }
    }

    public function fields_or_func($query)
    {
        return $query;
    }

    public function custom_func($query)
    {
        return $query;
    }

    public function search_func($query)
    {
        return $query;
    }

    public function status_func($query)
    {
        if (count($this->status) > 0) {
            foreach ($this->status as $key => $v) {
                if (count($this->join) > 0 && count($this->with) == 0) {
                    $query->where($this->prefix . '.' . $key, $v);
                } else {
                    $query->where($key, $v);
                }
            }
        }
    }

    public function other_function($query)
    {
        return $query;
    }

    public function fields_func($query)
    {
        return $query;
    }

    public function add()
    {
        try {
            if ($this->request->isPost()) {
                isset_json($this->data['data']);
                $this->filter_data();
                foreach ($this->data['data'] as $k => $v) {
                    try {
                        foreach ($v as $key => $value) {
                            foreach ($this->status as $kk => $vv) {
                                if ($key == $kk) {
                                    unset($this->data['data'][$k][$key]);
                                }
                            }
                        }
                        $this->validate($v, $this->rules, $this->message);
                    } catch (\Exception $exception) {
                        uapi_exit($exception->getMessage());
                    }
                }
                if ($this->debug) {
                    $res = $this->redis_list('batch_func', $this->data['data'], 50);
                    api_exit($res);
                } else {
                    Db::startTrans();
                    try {
                        $res = $this->redis_list('batch_func', $this->data['data'], 50);
                        Db::commit();
                        if ($this->return_res) {
                            return $res;
                        }
                        api_exit($res);
                    } catch (\Exception $e) {
                        Db::rollback();
                        uapi_exit($e->getMessage());
                    }
                }
            }
        } catch (\Exception $exception) {
            uapi_exit($exception->getMessage());
        }

    }

    protected function filter_data()
    {
        if (count($this->filter)) {
            foreach ($this->data as $key => $v) {
                if (in_array($key, $this->filter)) {
                    unset($this->data[$key]);
                }
            }
        }
    }

    /*
      * @param
      * $static_func_name 静态函数名称 传入参数为被切割的索引二维数组
      * $data  索引二维数组
      * $num  以多少数量为切割
      * @content
      * 消息队列批量插入/修改
      * 解决插入/修改数据量过大导致数据库连接超时
     */
    protected function redis_list($static_func_name, array $data = [], int $num = 50)
    {
        self::$static_model = $this->model;
        self::$static_id = $this->idName;
        $md5 = get_called_class() . md5(uniqid());
        if (count($data) > $num && $this->redis) {
            $arr_data = [];
            $floor = floor(count($data) / $num);
            $y = count($data) % $num;
            for ($i = 1; $i <= $floor; $i++) {
                array_push($arr_data, $num * $i);
            }
            array_push($arr_data, ($arr_data[count($arr_data) - 1] + $y));
            $arr = [];
            foreach ($data as $key => $v) {
                array_push($arr, $v);
                if (in_array(($key + 1), $arr_data)) {
                    array_shift($arr_data);
                    Cache::store('redis')->lPush($md5, json_encode($arr));
                    $arr = [];
                }
            }
            $while = true;
            $res = [];
            while ($while) {
                if ($info = Cache::store('redis')->rpop($md5)) {
                    $info = json_decode($info, true);
                    $arr = call_user_func_array([get_called_class(), $static_func_name], array($info, $md5));
                    $res = array_merge($res, $arr);
                } else {
                    $while = false;
                }
            }
        } else {
            $res = call_user_func_array([get_called_class(), $static_func_name], [$data]);
        }
        $redis_res = Cache::store('redis')->handler()->keys(get_called_class() . '*');
        if (count($redis_res)) {
            Cache::store('redis')->deleteMultiple($redis_res);
        }
        return $res;
    }

    public function edit()
    {
        try {
            if ($this->request->isPost()) {
                $this->rules[$this->idName] = 'require';
                $this->message[$this->idName . '.require'] = $this->idName . '不能为空';
                isset_json($this->data['data']);
                $this->filter_data();
                foreach ($this->data['data'] as $k => $v) {
                    try {
                        foreach ($v as $key => $value) {
                            foreach ($this->status as $kk => $vv) {
                                if ($key == $kk) {
                                    unset($this->data['data'][$k][$key]);
                                }
                            }
                        }
                        $this->validate($v, $this->rules, $this->message);
                    } catch (\Exception $exception) {
                        uapi_exit($exception->getMessage());
                    }
                }
                $ex=$this->model->where($this->idName,'in',array_column($this->data['data'],$this->idName))->column($this->idName);
                if(!count($ex)){
                    uapi_exit('要修改的数据不存在');
                }
                if ($this->debug) {
                    $this->redis_list('batch_func', $this->data['data'], 50);
                    api_exit([]);
                } else {
                    Db::startTrans();
                    try {
                        $this->redis_list('batch_func', $this->data['data'], 50);
                        Db::commit();
                        api_exit([]);
                    } catch (\Exception $e) {
                        Db::rollback();
                        uapi_exit($e->getMessage());
                    }
                }
            }
        } catch (\Exception $exception) {
            uapi_exit($exception->getMessage());
        }

    }

    public function get_info()
    {
        try {
            if ($this->request->isPost()) {
                $this->data['id'] = explode(',', $this->data['id']);
                $query = $this->model;
                if (count($this->with) == 1) {
                    $query = $query->hasWhere(array_keys($this->with)[0]);
                    $query = $query
                        ->with($this->with);
                } else {
                    if (is_array($this->field_info)) {
                        $this->fieldRaw = implode(',', $this->field_info);
                        $query = $query->field($this->field_info, true);
                    } else {
                        $query = $query->fieldRaw($this->field_info);
                    }
                    if (count($this->join) > 0) {
                        $query->withJoin($this->join, 'left');
                    }
                }
                if (count($this->join)) {
                    $this->idName = $this->prefix . '.' . $this->idName;
                }
                if (count($this->data['id']) > 1) {
                    $res = $query->where($this->idName, 'in', $this->data['id'])->select();
                } else {
                    $res = $query->where($this->idName, $this->data['id'][0])->select();
                }
                $res=$res->toArray();
                if (count($res)) {
                    api_exit($res);
                } else {
                    uapi_exit('无此记录');
                }
            }
        } catch (\Exception $exception) {
            uapi_exit($exception->getMessage());
        }


    }

    public function del()
    {
        if ($this->request->isPost()) {
            $this->id = $this->request->param($this->idName);
            if (!judge($this->id)) {
                uapi_exit('参数id不能为空');
            }
            $this->id = explode(',', $this->data[$this->idName]);
            if ($this->debug) {
                if (count($this->id) > 1) {
                    $this->model->where('id', 'in', $this->id)->update($this->del);
                } else {
                    $this->model->where('id', $this->id[0])->update($this->del);
                }
                api_exit([]);
            } else {
                Db::startTrans();
                try {
                    $this->del_before_func();
                    if (count($this->id) > 1) {
                        $res = $this->model->where('id', 'in', $this->id)->update($this->del);
                    } else {
                        $res = $this->model->where('id', $this->id[0])->update($this->del);
                    }
                    if (empty($res)) {
                        uapi_exit('无此记录');
                    }
                    $this->del_after_func();
                    Db::commit();
                    api_exit([]);
                } catch (\Exception $e) {
                    Db::rollback();
                    uapi_exit($e->getMessage());
                }
                api_exit([]);
            }

        }
    }

    protected function del_before_func()
    {

    }

    protected function del_after_func()
    {

    }

    protected function initialize()
    {
        $this->account_info = $this->request->account_info;
        $this->data = $this->request->except([config('base.sign')]);
        $this->del = config('base.del');
        if (!empty($this->prefix) && count($this->join)) {
            $this->fieldRaw = explode(',', $this->fieldRaw);
            foreach ($this->fieldRaw as $key => $v) {
                if ($v != $this->idName)
                    $this->fieldRaw[$key] = $this->prefix . '.' . $v;
            }
            $this->fieldRaw = implode(',', $this->fieldRaw);
        }
    }


}
